﻿using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using Wsq.Utils.DoNetFramework40.Socket.tools;

namespace Wsq.Utils.DoNetFramework40.Socket
{
    public class Server
    {

        private int m_maxConnectNum;    //最大连接数
        BufferManager m_bufferManager;
        System.Net.Sockets.Socket listenSocket;            //监听Socket  
        int m_clientCount;              //连接的客户端数量  
        const int opsToAlloc = 2;
        Semaphore m_maxNumberAcceptedClients;
        SocketEventPool m_pool;
        //SocketAsyncEventArgs acceptEventArg;

        public int ClientCount
        {
            get { return m_clientCount; }
        }

        #region 定义委托  

        /// <summary>  
        /// 客户端连接  
        /// </summary>
        public delegate void OnClientConnected(SocketAsyncEventArgs e);
        /// <summary>  
        /// 客户端连接断开
        /// </summary>
        public delegate void OnClientDisConnected(SocketAsyncEventArgs e);

        /// <summary>  
        /// 接收到客户端的数据  
        /// </summary>  
        /// <param name="token">客户端</param>  
        /// <param name="buff">客户端数据</param>  
        public delegate void OnReceiveData(SocketAsyncEventArgs e, byte[] buff);

        #endregion

        #region 定义事件  
        /// <summary>  
        /// 客户端连接事件  
        /// </summary>  
        public event OnClientConnected ClientConnected;
        /// <summary>  
        /// 客户端连接断开事件  
        /// </summary>  
        public event OnClientDisConnected ClientDisConnected;

        /// <summary>  
        /// 接收到客户端的数据事件  
        /// </summary>  
        public event OnReceiveData ReceiveClientData;


        #endregion

        #region 定义属性  


        #endregion

        /// <summary>  
        /// 构造函数  
        /// </summary>  
        /// <param name="numConnections">最大连接数</param>  
        public Server(int numConnections, int receiveBufferSize)
        {
            m_clientCount = 0;
            m_maxConnectNum = numConnections;
            // allocate buffers such that the maximum number of sockets can have one outstanding read and   
            //write posted to the socket simultaneously    
            m_bufferManager = new BufferManager(receiveBufferSize * numConnections * opsToAlloc, receiveBufferSize);
            m_pool = new SocketEventPool(numConnections);
            m_maxNumberAcceptedClients = new Semaphore(numConnections, numConnections);
            Init();
        }

        /// <summary>  
        /// 初始化  
        /// </summary>  
        private void Init()
        {
            m_bufferManager.InitBuffer();
            // Allocates one large byte buffer which all I/O operations use a piece of.  This gaurds   
            // against memory fragmentation  
            // preallocate pool of SocketAsyncEventArgs objects  
            SocketAsyncEventArgs readWriteEventArg;

            for (int i = 0; i < m_maxConnectNum; i++)
            {
                readWriteEventArg = new SocketAsyncEventArgs();
                readWriteEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(IO_Completed);
                m_bufferManager.SetBuffer(readWriteEventArg);
                // add SocketAsyncEventArg to the pool  
                m_pool.Push(readWriteEventArg);
            }
        }



        /// <summary>  
        /// 启动服务  
        /// </summary>  
        /// <param name="localEndPoint"></param>  
        public bool Start(IPEndPoint localEndPoint)
        {
            try
            {
                listenSocket = new System.Net.Sockets.Socket(localEndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
                listenSocket.Bind(localEndPoint);
                // start the server with a listen backlog of 100 connections  
                listenSocket.Listen(m_maxConnectNum);
                // post accepts on the listening socket  
                StartAccept(null);
                return true;
            }
            catch (Exception)
            {
                return false;
            }
        }

        /// <summary>  
        /// 停止服务  
        /// </summary>  
        public void Stop()
        {
            
            try
            {
                listenSocket.Shutdown(SocketShutdown.Both);
            }
            catch (Exception) { }

            listenSocket.Close();
        }


        public void CloseClient(SocketAsyncEventArgs e)
        {
            try
            {
                e.AcceptSocket.Shutdown(SocketShutdown.Both);
            }
            catch (Exception) { }
        }


        // Begins an operation to accept a connection request from the client   
        //  
        // <param name="acceptEventArg">The context object to use when issuing   
        // the accept operation on the server's listening socket</param>  
        public void StartAccept(SocketAsyncEventArgs acceptEventArg)
        {
            if (acceptEventArg == null)
            {
                acceptEventArg = new SocketAsyncEventArgs();
                acceptEventArg.Completed += new EventHandler<SocketAsyncEventArgs>(AcceptEventArg_Completed);
                //this.acceptEventArg= acceptEventArg;
            }
            else
            {
                // socket must be cleared since the context object is being reused  
                acceptEventArg.AcceptSocket = null;
            }

            m_maxNumberAcceptedClients.WaitOne();
            if (!listenSocket.AcceptAsync(acceptEventArg))
            {
                ProcessAccept(acceptEventArg);
            }
        }

        // This method is the callback method associated with Socket.AcceptAsync   
        // operations and is invoked when an accept operation is complete  
        //  
        void AcceptEventArg_Completed(object sender, SocketAsyncEventArgs e)
        {
            ProcessAccept(e);
        }

        private void ProcessAccept(SocketAsyncEventArgs e)
        {
            try
            {
                Interlocked.Increment(ref m_clientCount);
                // Get the socket for the accepted client connection and put it into the   
                //ReadEventArg object user token  
                SocketAsyncEventArgs readEventArgs = m_pool.Pop();
                readEventArgs.AcceptSocket = e.AcceptSocket;
                if (ClientConnected!=null)
                {
                    ClientConnected(readEventArgs);
                }
                
                if (!e.AcceptSocket.ReceiveAsync(readEventArgs))
                {
                    ProcessReceive(readEventArgs);
                }
            }
            catch (Exception me)
            {
                //RuncomLib.Log.LogUtils.Info(me.Message + "\r\n" + me.StackTrace);
                Console.WriteLine(me.Message);
            }

            // Accept the next connection request  
            if (e.SocketError == SocketError.OperationAborted) return;
            StartAccept(e);
        }


        void IO_Completed(object sender, SocketAsyncEventArgs e)
        {
            // determine which type of operation just completed and call the associated handler  
            switch (e.LastOperation)
            {
                case SocketAsyncOperation.Receive:
                    ProcessReceive(e);
                    break;
                case SocketAsyncOperation.Send:
                    ProcessSend(e);
                    break;
                default:
                    throw new ArgumentException("The last operation completed on the socket was not a receive or send");
            }

        }


        // This method is invoked when an asynchronous receive operation completes.   
        // If the remote host closed the connection, then the socket is closed.    
        // If data was received then the data is echoed back to the client.  
        //  
        private void ProcessReceive(SocketAsyncEventArgs e)
        {
            try
            {
                // check if the remote host closed the connection  
                if (e.BytesTransferred > 0 && e.SocketError == SocketError.Success)
                {
                    //读取数据  
                    byte[] data = new byte[e.BytesTransferred];
                    Array.Copy(e.Buffer, e.Offset, data, 0, e.BytesTransferred);
                    if (ReceiveClientData!=null)
                    {
                        ReceiveClientData(e, data);
                    }
                    //继续接收. 为什么要这么写,请看Socket.ReceiveAsync方法的说明  
                    if (!e.AcceptSocket.ReceiveAsync(e))
                        this.ProcessReceive(e);
                }
                else
                {
                    CloseClientSocket(e);
                }
            }
            catch (Exception xe)
            {
                //RuncomLib.Log.LogUtils.Info(xe.Message + "\r\n" + xe.StackTrace);
                Console.WriteLine(xe.Message);
                Console.WriteLine(xe.StackTrace);
            }
        }

        // This method is invoked when an asynchronous send operation completes.    
        // The method issues another receive on the socket to read any additional   
        // data sent from the client  
        //  
        // <param name="e"></param>  
        private void ProcessSend(SocketAsyncEventArgs e)
        {
            if (e.SocketError == SocketError.Success)
            {
                // read the next block of data send from the client  
                bool willRaiseEvent = e.AcceptSocket.ReceiveAsync(e);
                if (!willRaiseEvent)
                {
                    ProcessReceive(e);
                }
            }
            else
            {
                CloseClientSocket(e);
            }
        }

        //关闭客户端  
        private void CloseClientSocket(SocketAsyncEventArgs e)
        {
            // close the socket associated with the client  
            try
            {
                e.AcceptSocket.Shutdown(SocketShutdown.Send);
            }
            catch (Exception) { }
            e.AcceptSocket.Close();
            // decrement the counter keeping track of the total number of clients connected to the server  
            Interlocked.Decrement(ref m_clientCount);
            if (ClientDisConnected!=null)
            {
                ClientDisConnected(e);
            }
            m_maxNumberAcceptedClients.Release();
            m_pool.Push(e);
        }



        /// <summary>  
        /// 对数据进行打包,然后再发送  
        /// </summary>  
        /// <param name="token"></param>  
        /// <param name="message"></param>  
        /// <returns></returns>  
        public void SendMessage(SocketAsyncEventArgs e, byte[] message)
        {
            if (e == null || e.AcceptSocket == null || !e.AcceptSocket.Connected)
                return;
            try
            {
                e.AcceptSocket.Send(message);  //这句也可以发送, 可根据自己的需要来选择  
                ////新建异步发送对象, 发送消息  
                //SocketAsyncEventArgs sendArg = new SocketAsyncEventArgs();
                //sendArg.SetBuffer(buff, 0, buff.Length);  //将数据放置进去.  
                //e.AcceptSocket.SendAsync(sendArg);
            }
            catch (Exception ex)
            {
                //RuncomLib.Log.LogUtils.Info("SendMessage - Error:" + e.Message);
                Console.WriteLine(ex.Message);
            }
        }
    }
}
